package close.mazegame.screens;

import close.mazegame.Debug;
import close.mazegame.InputHandler;
import close.mazegame.MazeGame;
import close.mazegame.entity.player.PlayerAttributes;
import close.mazegame.input.uiinput.UIInput.UIKeys;
import close.mazegame.level.Level;
import close.mazegame.level.Level.LevelState;
import close.mazegame.level.MazeLevel;
import close.mazegame.tile.Tile;

import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Interpolation;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;

public class GameScreen extends Screen {

	//Normal camera size, in box2d units
	private static final float CAMERA_WIDTH = 16.0f;
	private static final float CAMERA_HEIGHT = 9.0f;
	
	//Timescale for level complete zoom out
	private final float timeScaleLevelComplete = 5;
	private float timeLevelComplete = 0;

	private Level level;
	private int levelWidth = 22;
	private int levelHeight = 12;

	private OrthographicCamera cam;
	private OrthographicCamera pixelCam;
	Box2DDebugRenderer debugRenderer = new Box2DDebugRenderer();

	private SpriteBatch gameSpriteBatch; // For rendering things that are in the game world
	private SpriteBatch fixedSpriteBatch; // For rendering things that are overlaid on the game world (i.e. HUD)
	private SpriteBatch pixelSpriteBatch; // For rendering fonts to the gameworld

	public GameScreen() {
		super();

		this.cam = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
		level = new MazeLevel(cam, levelWidth, levelHeight);
		this.cam.update();

		this.pixelCam = new OrthographicCamera(MazeGame.WIDTH, MazeGame.HEIGHT);
		this.pixelCam.update();
	}

	public void init(MazeGame mazeGame) {
		this.mazeGame = mazeGame;
		font = new BitmapFont();
		gameSpriteBatch = new SpriteBatch();
		fixedSpriteBatch = new SpriteBatch();
		pixelSpriteBatch = new SpriteBatch();
		spriteBatch = gameSpriteBatch;
	}

	public void dispose() {
		font.dispose();
		fixedSpriteBatch.dispose();
		gameSpriteBatch.dispose();
		pixelSpriteBatch.dispose();
	}

	@Override
	public void render() {
		updateCams();
		gameSpriteBatch.setProjectionMatrix(cam.combined);
		pixelSpriteBatch.setProjectionMatrix(pixelCam.combined);

		// Render gameworld stuff
		spriteBatch = gameSpriteBatch;
		spriteBatch.begin();
		level.render(this);
		spriteBatch.end();

		// Render lights
		if (!Debug.forceLight()) {
			level.renderLights();
		}

		// Render ui stuff
		spriteBatch = fixedSpriteBatch;
		spriteBatch.begin();
		level.renderUI(this);
		spriteBatch.end();

		// Render debug on world
		if (Debug.box2DDebug()) {
			spriteBatch = pixelSpriteBatch;
			spriteBatch.begin();
			drawDebug();
			spriteBatch.end();
		}
		spriteBatch = fixedSpriteBatch;
	}

	@Override
	public void update(float delta) {
		if(level.getState() == LevelState.COMPLETE){
			setScreen(new LevelCompleteScreen(this));
		} else if(level.getState() == LevelState.GAMEOVER){
			setScreen(new GameOverScreen(this));
		}
		level.update(delta, cam);
		if (InputHandler.uiInput.isTyped(UIKeys.UI_ESCAPE)) {
			setScreen(new PauseScreen(this));
		}
	}
	
	//Steps level complete transition forward by delta
	public void updateLevelComplete(float delta){
		if(cam.viewportHeight < level.getHeight() || cam.viewportWidth < level.getWidth()){
			timeLevelComplete += delta;
			cam.viewportHeight = Interpolation.linear.apply(CAMERA_HEIGHT, level.getWidth()*9.0f/16.0f, timeLevelComplete/timeScaleLevelComplete);
			cam.viewportWidth = Interpolation.linear.apply(CAMERA_WIDTH, level.getWidth(), timeLevelComplete/timeScaleLevelComplete);
		}
		level.updateLevelComplete(delta, cam);
	}

	@Override
	public boolean isOverlay() {
		return false;
	}

	private void updateCams() {
		Vector2 pPos = level.getPlayerPosition();
		float xCam = pPos.x;
		float yCam = pPos.y;

		if (xCam < cam.viewportWidth / 2f)
			xCam = cam.viewportWidth / 2f;
		else if (xCam > level.getWidth() - cam.viewportWidth / 2f)
			xCam = level.getWidth() - cam.viewportWidth / 2f;

		if (yCam < cam.viewportHeight / 2f)
			yCam = cam.viewportHeight / 2f;
		else if (yCam > level.getHeight() - cam.viewportHeight / 2f)
			yCam = level.getHeight() - cam.viewportHeight / 2f;

		cam.position.set(xCam, yCam, 0);
		cam.update();
		float ppux = MazeGame.WIDTH / cam.viewportWidth; // Pixels per box2D unit, x
		float ppuy = MazeGame.HEIGHT / cam.viewportHeight; // Pixels per box2D unit, y
		pixelCam.position.set(xCam * ppux, yCam * ppuy, 0);
		pixelCam.update();
	}

	private void drawDebug() {
		Tile[][] tiles = level.getTiles();
		for (Tile[] tiles1D : tiles) {
			for (Tile t : tiles1D) {
				Vector2 pos = t.getPosition();
				int dist = level.getPathfinder().getDistance((int) pos.x, (int) pos.y);
				String distStr = String.valueOf(dist);
				float ppux = MazeGame.WIDTH / cam.viewportWidth;
				float ppuy = MazeGame.HEIGHT / cam.viewportHeight;
				drawString(distStr, pos.x * ppux, pos.y * ppuy, 1.0f);
			}
		}
		spriteBatch.flush();
		debugRenderer.render(level.getWorld(), cam.combined);
	}

	public void nextLevel() {
		cam.viewportHeight = CAMERA_HEIGHT;
		cam.viewportWidth = CAMERA_WIDTH;
		timeLevelComplete = 0;
		PlayerAttributes pa = level.getAttributes();
		level.dispose();
		level = new MazeLevel(cam, levelWidth, levelHeight, pa);
	}

}
